﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace Davsha
{
    class Pedal : IMessageFilter
    {
        //private const string DeviceName = "\\\\?\\HID#VID_0C45&PID_7403&MI_00&Col01#";
        private string _uniuqStringInDeviceName;

        public event DevicePressEventHandler PedalPress;
        public event DevicePressEventHandler PedalDoublePress;
        public event ErrorEventHandler Error;
        
        private InputDevice _inputDevice;
        private Timer _doubleClickTimer;
        private bool _isFirstClick;
        private bool _isDoubleClick;
        private int _milliseconds;
        private int _doubleClickTimerInterval;
        private DeviceInfo _pedalInfo;

        public Pedal(IntPtr windowHandleToAcceptMessages, DeviceInfo pedalInfo)
        {
            PedalInfo = pedalInfo;
            
            _inputDevice = new InputDevice(windowHandleToAcceptMessages);
            _inputDevice.EnumerateDevices();
            
            MinTimeBetweenPresses = 10;
            DoubleClickTime = 300;
            
            _isFirstClick = true;
        
            _doubleClickTimerInterval = 100;

            _doubleClickTimer = new Timer();
            _doubleClickTimer.Interval = _doubleClickTimerInterval;
            _doubleClickTimer.Tick += new EventHandler(DoubleClickTimer_Tick);

            Application.AddMessageFilter(this);
            _inputDevice.KeyPressed += new InputDevice.DeviceEventHandler(InputDevice_KeyPressed);
        }

        public DeviceInfo PedalInfo
        {
            get
            {
                return _pedalInfo;
            }
            set
            {
                _pedalInfo = value;
                _uniuqStringInDeviceName = string.Format("VID_{0:X4}&PID_{1:X4}", _pedalInfo.VendorId, _pedalInfo.ProductId);
            }
        }

        public int DoubleClickTime
        {
            get;
            set;
        }

        /// <summary>
        /// Meant to cancel multiple keys sent by single pedal press.
        /// </summary>
        public int MinTimeBetweenPresses
        {
            get;
            set;
        }

        public bool IsIdentifying
        {
            get;
            set;
        }

        public bool PreFilterMessage(ref Message message)
        {
            return _inputDevice.ProcessMessage(message);
        }

        private void InputDevice_KeyPressed(object sender, InputDevice.KeyControlEventArgs e)
        {
            try
            {
                if (IsIdentifying)
                {
                    RaisePedalPress(DeviceInfo.FromDeviceName(e.Keyboard.deviceName));
                    return;
                }

                if (IsPedalDevice(e.Keyboard.deviceName))
                {
                    if (_isFirstClick)
                    {
                        _isFirstClick = false;

                        _doubleClickTimer.Start();
                    }
                    else
                    {
                        // If more key pressed event arrived in short time period, it means the pedal is configured to send several key strokes in each press, 
                        // this is meant to cancel that and raise only one press event.
                        if (_milliseconds < MinTimeBetweenPresses)
                        {
                            return;
                        }

                        // Verify that the mouse click is within the double click period

                        if (_milliseconds < DoubleClickTime)
                        {
                            _isDoubleClick = true;
                        }
                    }

                    e.Handled = true;
                }
            }
            catch (Exception x)
            {
                RaiseError(x);
            }
        }

        private void DoubleClickTimer_Tick(object sender, EventArgs e)
        {
            _milliseconds += _doubleClickTimerInterval;

            // The timer has reached the double click time limit.
            if (_milliseconds >= DoubleClickTime)
            {
                _doubleClickTimer.Stop();

                if (_isDoubleClick)
                {
                    RaisePedalDoublePress();
                }
                else
                {
                    RaisePedalPress();
                }

                // Allow the MouseDown event handler to process clicks again.
                _isFirstClick = true;
                _isDoubleClick = false;
                _milliseconds = 0;
            }
        }

        private bool IsPedalDevice(string deviceName)
        {
            return deviceName.IndexOf(_uniuqStringInDeviceName, StringComparison.InvariantCultureIgnoreCase) >= 0;
        }

        private void RaisePedalPress()
        {
            RaisePedalPress(_pedalInfo);
        }

        private void RaisePedalPress(DeviceInfo deviceInfo)
        {
            if (PedalPress != null)
            {
                PedalPress(this, new DevicePressEventArgs(deviceInfo));
            }
        }

        private void RaisePedalDoublePress()
        {
            if (PedalDoublePress != null)
            {
                PedalDoublePress(this, new DevicePressEventArgs(_pedalInfo));
            }
        }

        private void RaiseError(Exception error)
        {
            if (Error != null)
            {
                Error(this, new ErrorEventArgs(error));
            }
        }
    }
}
